package io.wongxd.solution.util

import android.view.View
import android.widget.Checkable
import androidx.annotation.IdRes
import io.wongxd.solution.logger.WLogUtil

/** 判断脚标是否在列表范围类 */
fun Int.inBounds(collection: Collection<*>): Boolean {
    return collection.isNotEmpty() && this in collection.indices
}

/** 判断脚标是否在列表范围类 */
fun Collection<*>.inBounds(position: Int): Boolean {
    return position.inBounds(this)
}

/** 判断脚标是否在列表范围类。线程安全 */
fun Int.inBoundsSafe(collection: Collection<*>): Boolean {
    synchronized(collection) {
        return collection.isNotEmpty() && this in collection.indices
    }
}

/** 判断脚标是否在列表范围类。线程安全 */
fun Collection<*>.inBoundsSafe(position: Int): Boolean {
    return position.inBoundsSafe(this)
}

/** 分割List结合 */
fun <T> MutableList<T>.split(limit: Int): MutableList<MutableList<T>> {
    val result = mutableListOf<MutableList<T>>()
    if (this.isEmpty() || limit <= 0) {
        result.add(this)
        return result
    }
    if (this.size <= limit) {
        result.add(this)
        return result
    }
    val splitNum = if (this.size % limit == 0) this.size / limit else this.size / limit + 1
    for (i in 0 until splitNum) {
        val value = if (i < splitNum - 1) {
            this.subList(i * limit, (i + 1) * limit)
        } else {
            this.subList(i * limit, this.size)
        }
        val list = mutableListOf<T>()
        list.addAll(value)
        result.add(list)
    }
    return result
}

/** try - catch */
fun <T> tryCatch(tryBlock: () -> T?, catchBlock: (() -> T?)? = null): T? {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        catchBlock?.invoke()
    }
}

suspend fun <T> tryCatchSuspend(
    tryBlock: suspend () -> T?,
    catchBlock: (suspend () -> T?)? = null
): T? {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        catchBlock?.invoke()
    }
}

/** try - catch */
fun <T> tryCatchIgnoreLog(block: () -> T?): T? {
    return try {
        block.invoke()
    } catch (ignore: Exception) {
        // ignore.printStackTrace()
        null
    }
}

/** try - catch */
suspend fun <T> tryCatchIgnoreLogSuspend(block: suspend () -> T?): T? {
    return try {
        block.invoke()
    } catch (ignore: Exception) {
        // ignore.printStackTrace()
        null
    }
}

/** try - catch. return notnull value */
fun <T> tryCatchNotNull(tryBlock: () -> T, catchBlock: () -> T): T {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        catchBlock.invoke()
    }
}

/** try - catch. return notnull value */
suspend fun <T> tryCatchNotNullSuspend(tryBlock: suspend () -> T, catchBlock: suspend () -> T): T {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        catchBlock.invoke()
    }
}

fun <T> tryCatchOpt(opt: T, tryBlock: () -> T): T {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        opt
    }
}

suspend fun <T> tryOptSuspend(opt: T, tryBlock: suspend () -> T): T {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        opt
    }
}

/** try - catch. return Unit */
fun tryIgnoreVoid(tryBlock: () -> Unit) {
    try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
    }
}

/** try - catch. return Unit */
suspend fun tryIgnoreVoidSuspend(tryBlock: suspend () -> Unit) {
    try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
    }
}

/** try - catch. return nullable value */
fun <T> tryIgnore(tryBlock: () -> T?): T? {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        null
    }
}

/** try - catch. return nullable value */
suspend fun <T> tryIgnoreSuspend(tryBlock: suspend () -> T?): T? {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        null
    }
}

/** try - catch - finally. return nullable value */
fun <T> tryCatchFinally(tryBlock: () -> T?, catchBlock: () -> T?, final: () -> T?): T? {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        catchBlock.invoke()
    } finally {
        final.invoke()
    }
}

/** try - catch - finally. return nullable value */
suspend fun <T> tryCatchFinallySuspend(
    tryBlock: suspend () -> T?,
    catchBlock: suspend () -> T?,
    final: suspend () -> T?
): T? {
    return try {
        tryBlock.invoke()
    } catch (ignore: Exception) {
        WLogUtil.errorTrace(ignore)
        catchBlock.invoke()
    } finally {
        final.invoke()
    }
}

/** try - finally. return nullable value */
fun <T> tryFinally(tryBlock: () -> T?, final: () -> T?): T? {
    return try {
        tryBlock.invoke()
    } finally {
        final.invoke()
    }
}

/** try - finally. return nullable value */
suspend fun <T> tryFinallySuspend(tryBlock: suspend () -> T?, final: suspend () -> T?): T? {
    return try {
        tryBlock.invoke()
    } finally {
        final.invoke()
    }
}

sealed class BooleanExt<out T>

class Yes<T>(val data: T) : BooleanExt<T>()

object No : BooleanExt<Nothing>()

inline fun <T> Boolean.yes(block: () -> T): BooleanExt<T> = when {
    this -> Yes(block())
    else -> No
}

inline fun <T> BooleanExt<T>.no(block: () -> T): T = when (this) {
    is Yes -> this.data
    else -> block()
}

inline fun <reified T : View> View.find(@IdRes id: Int): T = findViewById(id)

/** 防抖点击事件 */
inline fun <T : View> T.singleClick(time: Long = 800, crossinline block: (T) -> Unit) {
    this.forbidSimulateClick()
    setOnClickListener {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - lastClickTime > time || this is Checkable) {
            lastClickTime = currentTimeMillis
            block(this)
        }
    }
}

/** 双击事件 */
inline fun <T : View> T.doubleClick(crossinline block: (T) -> Unit) {
    this.forbidSimulateClick()
    var lastClickTimeStamp = 0L
    var lastDoubleClickTimeStamp = 0L
    setOnClickListener {
        val now = System.currentTimeMillis()
        // 上次的点击时间如果为初始值，那么肯定是第一次点击，先赋值为现在的时间
        if (lastClickTimeStamp == 0L) {
            lastClickTimeStamp = now
            return@setOnClickListener
        }
        // 如果上次双击的时间为初始值，肯定是第一次双击，直接赋值为现在的时间值，并且不拦截
        if (lastDoubleClickTimeStamp == 0L) {
            lastDoubleClickTimeStamp = now
        } else {
            // 如果上次双击的时间和现在双击的时间相距不足1秒，直接拦截
            if (now - lastDoubleClickTimeStamp <= 1_000) {
                lastClickTimeStamp = now
                return@setOnClickListener
            }
        }
        if (now - lastClickTimeStamp <= 1000) {
            block.invoke(this)
            lastDoubleClickTimeStamp = now
        }
        lastClickTimeStamp = now
    }
}

/** 兼容点击事件设置为this的情况 */
fun <T : View> T.singleClick(onClickListener: View.OnClickListener?, time: Long = 800) {
    this.forbidSimulateClick()
    setOnClickListener {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - lastClickTime > time || this is Checkable) {
            lastClickTime = currentTimeMillis
            onClickListener?.onClick(this)
        }
    }
}

// 扩展点击事件属性
inline var <T : View> T.lastClickTime: Long
    set(value) = setTag(1766613352, value)
    get() = getTag(1766613352) as? Long ?: 0

/** 屏蔽机器人模拟点击 */
fun View?.forbidSimulateClick() {
    if (null == this) return
    this.accessibilityDelegate = ForbidAccessibilityDelegate()
}